#include "LecteurPhraseAvecArbre.h"

#include <typeinfo>
#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
    nbErreur = 0;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    if (this->getNbErreur() == 0)
        cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::afficherEnC(unsigned short indentation) {
    TableSymboles ts;
    cout << " main (int argc, char *argv[]) {" << endl;
    ts = this->getTs();
    for (unsigned int i = 0; i < ts.getTable().size(); i++) {
        if (typeid (*ts.getTable()[i]) == typeid (SymboleValue) &&
                *(SymboleValue*) ts.getTable()[i] == "<VARIABLE>") {
            cout << " int ";
            ts.getTable()[i]->afficherEnC(indentation + 1);
            cout << ";" << endl;
        }
    }
    this->getArbre()->afficherEnC();
    cout << "}";
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::programme() {
     // <programme> ::= debut <seqInst> fin <EOF>

    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");

    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::seqInst() {
    // <seq_ins> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>" ||
            ls.getSymCour() == "si" ||
            ls.getSymCour() == "tantque" ||
            ls.getSymCour() == "repeter" ||
            ls.getSymCour() == "pour" ||
            ls.getSymCour() == "lire" ||
            ls.getSymCour() == "ecrire" ||
            ls.getSymCour() == "selon");
    // tant que le symbole courant est un debut possible d'instruction...

    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instEcrire> | <instLire> | <instSelon>
    try {
        if (ls.getSymCour() == "si")
            return instSi();
        else if (ls.getSymCour() == "tantque")
            return instTq();
        else if (ls.getSymCour() == "repeter")
            return instRepeter();
        else if (ls.getSymCour() == "pour")
            return instPour();
        else if (ls.getSymCour() == "lire")
            return instLire();
        else if (ls.getSymCour() == "ecrire")
            return instEcrire();
        else if (ls.getSymCour() == "selon")
            return instSelon();
        else if (ls.getSymCour() == "<VARIABLE>")
            return affectation();
        else {
            erreur("instruction");
            return 0;
        }
    } catch (ErreurException) {
        nbErreur++;
        while (ls.getSymCour() != ";") {
            ls.suivant();
        }
        return 0;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::affectation() {
     // <affectation> ::= <variable> = <expression>

    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();

    return new NoeudAffectation(var, exp);
}
////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instSi() {
    // <instSi> ::= si (<expBool>) <seqInst> { sinonsi (<expBool>) <seqInst> } [ sinon <seqInst> ] finsi
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* exp = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    NoeudInstSi* prem = new NoeudInstSi(exp, seq, NULL);
    NoeudInstSi* cour = prem;
    while (ls.getSymCour() == "sinonsi") {
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        exp = expBool();
        sauterSymCour(")");
        seq = seqInst();
        cour->setSinon(new NoeudInstSi(exp, seq, NULL));
        cour = (NoeudInstSi*) cour->getSinon();
    }
    if (ls.getSymCour() == "sinon") {
        sauterSymCour("sinon");
        seq = seqInst();
        cour->setSinon(seq);
    }
    sauterSymCour("finsi");

    return prem;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instTq() {
    // <instTq> ::= tantque (<expBool>) <seqInst> fintantque
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* exp = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("fintantque");

    return new NoeudInstTq(exp, seq);
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instRepeter() {
    // <instRepeter> ::= repeter <seqInst> jusqua (<expBool>)
    sauterSymCour("repeter");
    Noeud* seq = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* exp = expBool();
    sauterSymCour(")");

    return new NoeudInstRepeter(exp, seq);
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instPour() {
    // <instPour> ::= pour ( <affectation> ; <expBool> ; <affectation>) <seqInst> finpour
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* aff1 = affectation();
    sauterSymCour(";");
    Noeud* exp = expBool();
    sauterSymCour(";");
    Noeud* aff2 = affectation();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("finpour");

    return new NoeudInstPour(exp, seq, aff1, aff2);
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instLire() {
    // <instLire> ::= lire ( <variable> )
    sauterSymCour("lire");
    sauterSymCour("(");
    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour(")");

    return new NoeudInstLire(var);
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instEcrire() {
    // <instEcrire> ::= ecrire ( <expression> | <CHAINE> | <CARACTERE>)
    bool estChaine = false;
    Noeud* str = NULL;
    Noeud* exp = NULL;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<CHAINE>" || 
            ls.getSymCour() == "<CARACTERE>" ||
            ls.getSymCour() == "VARIABLE" ||
            ls.getSymCour() == "ENTIER" ||
            ls.getSymCour() == "REEL") {
        str = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        estChaine = true;
    } else
        exp = expression();
    sauterSymCour(")");
    if (estChaine)
        return new NoeudInstEcrire(str);

    else
        return new NoeudInstEcrire(exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instSelon() {
     // <instSelon> ::= selon ( <expression> | <CHAINE> | <CARACTERE>) cas (expression | <CHAINE> | <CARACTERE>) : <seqInst> 
                    // { cas (expression | <CHAINE> | <CARACTERE>) : <seqInst> } finchoix
    Noeud* exp = NULL;
    Noeud* val = NULL;
    Noeud* seq = NULL;
    sauterSymCour("selon");
    sauterSymCour("(");
     if (ls.getSymCour() == "<CHAINE>" || 
            ls.getSymCour() == "<CARACTERE>" ||
            ls.getSymCour() == "VARIABLE" ||
            ls.getSymCour() == "ENTIER" ||
            ls.getSymCour() == "REEL") {
        exp = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else
        exp = expression();
    sauterSymCour(")");
    // On rentre dans les cas
    sauterSymCour("cas");
    sauterSymCour("(");
     if (ls.getSymCour() == "<CHAINE>" || 
            ls.getSymCour() == "<CARACTERE>" ||
            ls.getSymCour() == "VARIABLE" ||
            ls.getSymCour() == "ENTIER" ||
            ls.getSymCour() == "REEL") {
        val = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else
        val = expression();
    sauterSymCour(")");
    sauterSymCour(":");
    seq = seqInst();
    NoeudOperateurBinaire* expBool = new NoeudOperateurBinaire((Symbole) "==", exp, val);
    NoeudInstSi* prem = new NoeudInstSi(expBool, seq, NULL);
    NoeudInstSi* cour = prem;
    while (ls.getSymCour() == "cas") {
        sauterSymCour("cas");
        sauterSymCour("(");
         if (ls.getSymCour() == "<CHAINE>" || 
            ls.getSymCour() == "<CARACTERE>" ||
            ls.getSymCour() == "VARIABLE" ||
            ls.getSymCour() == "ENTIER" ||
            ls.getSymCour() == "REEL") {
            val = ts.chercheAjoute(ls.getSymCour());
            ls.suivant();
        } else
            val = expression();
        sauterSymCour(")");
        sauterSymCour(":");
        seq = seqInst();
        expBool = new NoeudOperateurBinaire((Symbole) "==", exp, val);
        cour->setSinon(new NoeudInstSi(expBool, seq, NULL));
        cour = (NoeudInstSi*) cour->getSinon();
    }
    // cour->setSinon(new NoeudInstSi()); 
    sauterSymCour("finselon");

    return prem;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <terme> { <opAdd> <terme> }

    Noeud* ter = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {

        Symbole op = opAdd();
        Noeud* terDroit = terme();
        ter = new NoeudOperateurBinaire(op, ter, terDroit); // const. du noeud
    }
    return ter;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <expression> { <opRel> <expression> }
    Noeud* exp = expression();
    while (ls.getSymCour() == "==" ||
            ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<" ||
            ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">" ||
            ls.getSymCour() == ">=") {

        Symbole op = opRel();
        Noeud* expDroite = expression();
        exp = new NoeudOperateurBinaire(op, exp, expDroite); // const. du noeud
    }
    return exp;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <ENTIER> | <REEL> |  <VARIABLE>  | <opUnaire> <expBool> | (<expBool>)

    Noeud* fact = NULL;

    if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>" ||
            ls.getSymCour() == "<CHAINE>" || ls.getSymCour() == "<REEL>" ||
            ls.getSymCour() == "<CARACTERE>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-") {
        ls.suivant();
        // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), expBool());
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");

    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <oppMult> <facteur> }
    Noeud* fact = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {

        Symbole op = opMult();
        Noeud* factDroite = facteur();
        if (op == "/" && factDroite->getValeur() == 0)
            erreurDiv();
        fact = new NoeudOperateurBinaire(op, fact, factDroite); // const. du noeud
    }
    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::expBool() {
    // <expBool> ::= <expBoolEt> { ou <expBoolEt> }
    Noeud* exp = expBoolEt();
    while (ls.getSymCour() == "ou") {

        Symbole op = ls.getSymCour();
        ls.suivant();
        Noeud* expDroite = expBoolEt();
        exp = new NoeudOperateurBinaire(op, exp, expDroite); // const. du noeud
    }
    return exp;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::expBoolEt() {
    // <expBoolEt> ::= <relation> { et <relation> }
    Noeud* rela = relation();
    while (ls.getSymCour() == "et") {

        Symbole op = ls.getSymCour();
        ls.suivant();
        Noeud* relaDroite = relation();
        rela = new NoeudOperateurBinaire(op, rela, relaDroite); // const. du noeud
    }
    return rela;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd() {
    // <opAdd> ::= + | -
    Symbole op;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        op = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");

    return op;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() {
    // <opMult> ::= * | /
    Symbole op;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        op = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");

    return op;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=
    Symbole op;
    if (ls.getSymCour() == "==" ||
            ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<" ||
            ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">" ||
            ls.getSymCour() == ">=") {
        op = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opRel>");

    return op;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= - | non
    Symbole op;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        op = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");

    return op;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {

        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        throw SyntaxeException();
        //exit(0); // plus tard, on levera une exception
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {

    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    throw SyntaxeException();
    //exit(0); // plus tard, on levera une exception
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreurDiv() {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() - 1 << endl << "   Division par 0" << endl << endl;
    throw DivisionException();
    //exit(0); // plus tard, on levera une exception
}
